
#include "los_config.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <time.h>
#include <sys/mman.h>
#include <ctype.h>

#ifdef LOSCFG_SHELL
#include "shcmd.h"
#include "shell.h"
#endif

#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif
#endif /* __cplusplus */


#define OPEN_FILE "/dev/mem"
#define DDR_STAT_SIG SIGUSR1

#define PAGE_SIZE_MASK 0xfffff000

#define TIMER_INTERVAL 1

#define DDRC_BASE_ADDR 0x120d0000

#define DDRC_MAP_LENGTH 0x20000

#define DDRC0_ADDR 0x8000
#define DDRC1_ADDR 0x9000


#define DDRC_TEST_EN 0x010
#define DDRC_TEST7 0x26C
#define DDRC_TEST8 0x380
#define DDRC_TEST9 0x384
#define DDRC_MODE 0x050

#define reg_read(addr) (*(volatile unsigned int *)(addr))
#define reg_write(addr, val) (*(volatile unsigned int *)(addr) = (val))

static int fd;

static unsigned char *ddrc_base_addr;
static unsigned char *ddrc0_addr;
static unsigned char *ddrc1_addr;

static unsigned int timer_interval = TIMER_INTERVAL;
static unsigned int reg_value;
static unsigned int ddrc_num;
static unsigned int ddrc_freq = 450;

static unsigned int step = 0;

#undef IO_ADDRESS
#define IO_ADDRESS(addr, off) ((unsigned char *)(addr) + off)

static int ddrc_remap(unsigned int ddrc_num);
static void ddrc_unmap(unsigned int ddrc_num);
static void print_usage(void);
static int parse_args(int argc, char *argv[], unsigned int *second,
                      unsigned int *ddrc, unsigned int *freq);

#define read_ddrc_reg(addr, tmp1, tmp2) do {\
    tmp1 = reg_read(IO_ADDRESS(addr, DDRC_TEST7));\
    ddr_read = reg_read(IO_ADDRESS(addr, DDRC_TEST8));\
    ddr_write = reg_read(IO_ADDRESS(addr, DDRC_TEST9));\
    tmp1 = tmp1& 0x0fffffff;\
    tmp2 = ddr_read + ddr_write;\
} while (0)

static unsigned int get_bit_width(unsigned char *addr)
{
    unsigned int value = 0;

    value = reg_read(IO_ADDRESS(addr, 0x050));
    value = (value >> 4) & 0x3;

    return value;
}

static void ddr_statistic(int n)
{
    unsigned int ddr_read = 0;
    unsigned int ddr_write = 0;
    unsigned int tmp1 = 0;
    double tmp2 = 0.0;
    double ddrc0_rate = 0.0;
    double ddrc1_rate = 0.0;

    if (2 == step) {
        return;
    }

    if (0 == ddrc_num) {
        read_ddrc_reg(ddrc0_addr, tmp1, tmp2);
        ddrc0_rate = (tmp2 / tmp1) * 100 / 16;
        /* 0x01: 16bit; 0x10: 32bit */
        if (0x01 == get_bit_width(ddrc0_addr)) {
            ddrc0_rate = ddrc0_rate;
        }
        printf("ddrc0[%0.2f%%]\n", ddrc0_rate);
        reg_write(IO_ADDRESS(ddrc0_addr, DDRC_TEST7), reg_value);
    } else if (1 == ddrc_num) {
        read_ddrc_reg(ddrc1_addr, tmp1, tmp2);
        ddrc0_rate = (tmp2 / tmp1) * 100 / 16;
        if (0x01 == get_bit_width(ddrc1_addr)) {
            ddrc0_rate = ddrc0_rate;
        }
        printf("ddrc1[%0.2f%%]\n", ddrc0_rate);
        reg_write(IO_ADDRESS(ddrc1_addr, DDRC_TEST7), reg_value);
    } else if (2 == ddrc_num) {
        read_ddrc_reg(ddrc0_addr, tmp1, tmp2);
        ddrc0_rate = (tmp2 / tmp1) * 100 / 16;
        if (0x01 == get_bit_width(ddrc0_addr)) {
            ddrc0_rate = ddrc0_rate;
        }

        read_ddrc_reg(ddrc1_addr, tmp1, tmp2);
        ddrc1_rate = (tmp2 / tmp1) * 100 / 16;
        if (0x01 == get_bit_width(ddrc1_addr)) {
            ddrc1_rate = ddrc1_rate;
        }
        printf("ddrc0[%0.2f%%] ddrc1[%0.2f%%]\n", ddrc0_rate, ddrc1_rate);
        reg_write(IO_ADDRESS(ddrc0_addr, DDRC_TEST7), reg_value);
        reg_write(IO_ADDRESS(ddrc1_addr, DDRC_TEST7), reg_value);

    } else
        ;

    /* enable ddr bandwidth statistic */
    reg_write(IO_ADDRESS(ddrc0_addr, DDRC_TEST_EN), 0x1);

    return;
}

static void ddr_ctrl_c(int n)
{
    /* disable ddr bandwidth statistic */
    reg_write(IO_ADDRESS(ddrc0_addr, DDRC_TEST_EN), 0x0);
    ddrc_unmap(ddrc_num);
}

static int ddrc_remap(unsigned int ddrc_num)
{
    unsigned int phy_addr_in_page = 0;

    phy_addr_in_page = DDRC_BASE_ADDR & PAGE_SIZE_MASK;

    ddrc_base_addr = (unsigned char *)mmap(NULL, DDRC_MAP_LENGTH,
                                           PROT_READ | PROT_WRITE, MAP_SHARED,
                                           fd, phy_addr_in_page);

    if (ddrc_base_addr == MAP_FAILED) {
        printf("ddr%d statistic mmap failed.\n", ddrc_num);
        return -1;
    }

    ddrc0_addr = ddrc_base_addr + DDRC0_ADDR;
    ddrc1_addr = ddrc_base_addr + DDRC1_ADDR;

    return 0;
}

static void ddrc_unmap(unsigned int ddrc_num)
{
    munmap(ddrc_base_addr, DDRC_MAP_LENGTH);
    ddrc_base_addr = NULL;

    return;
}

static void print_usage(void)
{
    printf("NAME\n");
    printf("  ddrs - ddr statistic\n\n");
    printf("DESCRIPTION\n");
    printf("  Statistic percentage of occupation of ddr.\n\n");
    printf("  -d, --ddr controller\n");
    printf("      which ddr controller you want statistic. \"0\" statistic ddr controller 0.\n");
    printf("  -f, --freq\n");
    printf("      the ddr controller provides the ddr component freq, please set the freq referring to the chip.\n");
    printf("      (Obtain the frequency  of the DDR controller from the table name and divide it by 4.\n");
    printf("       For example, the frequency in the table name is 1800M, then 1800 divided by 4 to 450, that is, the -f parameter is 450.)\n");
    printf("       \"450\" as default.\n");
    printf("  -i, --statistics interval\n");
    printf("      the range is 1~3 second, 1 second as default.\n");
    printf("  -h, --help\n");
    printf("      display this help\n");
    PRINTK("  -q, close\n");
    PRINTK("      close and exit\n");
    printf("  eg:\n");
    printf("      $ hiddrs -d 0 -f 450 -i 1\n");
    printf("      or\n");
    printf("      $ hiddrs\n");
    return;
}

#define check_digit() \
    do {\
        for (k = 0; k < strlen(argv[i + 1]); ++k) {\
            if(0 == isdigit(argv[i + 1][k])) {\
                flags = 1;\
                break;\
            }\
        }\
    } while (0)

static int parse_args(int argc, char *argv[], unsigned int *second,
                      unsigned int *ddrc, unsigned int *freq)
{
    int i = 0, k = 0;
    int flags = 0;
    int _ddrc = 0;
    int _second = 0;
    int _freq = 0;

    if ((argc % 2) != 0) {
        goto ERROR;
    }

    for (i = 0; i < argc; i += 2) {
        flags = 0;
        if (0 == strcmp("-d", argv[i]) ||
                0 == strcmp("--ddrc",  argv[i])) {
            check_digit();
            if (!flags) {
                _ddrc = atoi(argv[i + 1]);
                if (_ddrc < 0 || _ddrc > 2) {
                    goto ERROR;
                }
                *ddrc = _ddrc;
            } else {
                goto ERROR;
            }
        } else if (0 == strcmp("-f", argv[i]) ||
                   0 == strcmp("--freq", argv[i])) {
            check_digit();
            if (!flags) {
                _freq = atoi(argv[i + 1]);
                if (_freq < 0) {
                    goto ERROR;
                }
                *freq = _freq;
            } else {
                goto ERROR;
            }
        } else if (0 == strcmp("-i",  argv[i]) ||
                   0 == strcmp("--interval", argv[i])) {
            check_digit();
            if (!flags) {
                _second = atoi(argv[i + 1]);
                if (_second < 0 || _second > 3) {
                    goto ERROR;
                }
                *second = _second;
            } else {
                goto ERROR;
            }
        } else if (0 == strcmp("-h",  argv[i]) ||
                   0 == strcmp("--help", argv[i])) {
            print_usage();
        } else {
            goto ERROR;
        }
    }

    return 0;
ERROR:
    print_usage();
    return -1;
}

//int main(int argc, char *argv[])
static int hiddrs_config(void)
{
    int ret = 0;
    unsigned int perf_prd;

    ret = ddrc_remap(ddrc_num);
    if (ret) {
        goto ERR;
    }

    perf_prd = ddrc_freq * 1000000 / 16;

    reg_value = timer_interval * perf_prd;
    reg_value &= 0x0fffffff;

    reg_value |= (0x1 << 28);

    if (0 == ddrc_num) {
        reg_write(IO_ADDRESS(ddrc0_addr, DDRC_TEST7), reg_value);
    } else if (1 == ddrc_num) {
        reg_write(IO_ADDRESS(ddrc1_addr, DDRC_TEST7), reg_value);
    } else if (2 == ddrc_num) {
        reg_write(IO_ADDRESS(ddrc0_addr, DDRC_TEST7), reg_value);
        reg_write(IO_ADDRESS(ddrc1_addr, DDRC_TEST7), reg_value);
    } else
        ;

    /* enable ddr bandwidth statistic */
    reg_write(IO_ADDRESS(ddrc0_addr, DDRC_TEST_EN), 0x1);

ERR:
    return ret;
}

static void hiddrs(void)
{
    PRINTK("===== ddr statistic =====\n");

    while (1) {
        (void)sleep(timer_interval);
        if (2 == step) {
            break;
        }
        ddr_statistic(0);
    }
    ddr_ctrl_c(0);
    PRINTK("hiddrs end. \n");
    step = 0;
}

static int cmd_hiddrs(int argc, char *argv[])
{
    static unsigned int hiddrs_taskid;
    UINT32 ret = 0;
    TSK_INIT_PARAM_S hiddrsTask;
    memset_s(&hiddrsTask, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));

    if ((argc > 0) &&
            ((0 == strcmp("close", argv[0])) || (0 == strcmp("-q", argv[0])))) {
        if (1 == step) {
            step = 2;
        }
    } else {
        ret = (UINT32)parse_args(argc, &argv[0], &timer_interval, &ddrc_num, &ddrc_freq);
        if (ret) {
            return ret;
        }
        ret = hiddrs_config();
        if (ret) {
            return ret;
        }

        if (0 == step) {
            hiddrsTask.pfnTaskEntry = (TSK_ENTRY_FUNC)hiddrs;
            hiddrsTask.uwStackSize  = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
            hiddrsTask.pcName = "hiddrs_task";
            hiddrsTask.usTaskPrio = 8;
            hiddrsTask.uwResved   = LOS_TASK_STATUS_DETACHED;
            ret = LOS_TaskCreate(&hiddrs_taskid, &hiddrsTask);
            PRINTK("hiddrs begin. \n");

            if (LOS_OK != ret) {
                PRINTK("hiddrs_task create failed ! \n");
                return -1;
            }
            step = 1;
        }
    }
    return ret;
}

#ifdef LOSCFG_SHELL
SHELLCMD_ENTRY(hiddrs_shellcmd, CMD_TYPE_EX, "hiddrs", 7, (CMD_CBK_FUNC)cmd_hiddrs); /*lint !e19 */
#endif

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */




